Since 2015, JavaScript has improved immensely.
It’s much more pleasant to use it now than ever.
In this article, we’ll look at metaprogramming with JavaScript proxies.
Handle Negative Array Indices
With proxies, we can enhance arrays by letting us access arrays with negative indexes.
For instance, we can write:
const handler = {
get(target, propKey, receiver) {
const index = Number(propKey);
if (index < 0) {
propKey = String(target.length + index);
}
return target[propKey];
}
};
const target = [];
target.push(1, 2, 3);
const arr = new Proxy(target, handler);
console.log(arr[-1])
We create the handler
object with the get
method.
If we have an index
that’s less than 0, then we make it positive by adding the target.length
to it.
Then we return the property of array with the index of the array.
Next, we create the target
array with the arguments.
Then we return a proxy which is an array-like object that we can use negative indexes with.
Setting Properties
The set
method in the handler
object lets us control how we set properties in an object.
For example, we can write:
function createArray(callback) {
const array = [];
return new Proxy(array, {
set(target, propertyKey, value, receiver) {
callback(propertyKey, value);
return Reflect.set(target, propertyKey, value, receiver);
}
});
}
const arr = createArray(
(key, value) => console.log(key, value));
arr.push('a');
to create an array where we can watch the operations of it.
We have a createArray
function that takes a callback
that’s called whenever we set a value on the array.
The callback
is called with the propertyKey
and value
.
Then we see the array operations logged as they’re being done.
Revocable References
We can create revocable references so that we can access objects only when we’re allowed.
For example, we can write:
const target = {};
const handler = {};
const {
proxy,
revoke
} = Proxy.revocable(target, handler);
target
is an object that we can create a proxy with.
handler
is an object to let us intercept and change the object operations.
proxy
is the proxy returned.
And revoke
is a function that lets us revoke access to the proxy.
Meta Object
JavaScript objects have hidden properties that let us get objects.
There’s a method to let us get the object’s properties and return it.
They can trap get and call operations.
Enforcing Invariants for Proxies
We can enforce various kinds of invariants with proxies.
One thing we can do is to disable the extensibility of objects.
We can use the Object.preventExtensions
method to disable adding properties to objects.
For instance, we can write:
'use strict'
const obj = Object.preventExtensions({});
obj.bar = 'foo';
to stop us from adding properties to an object.
If strict mode is on, then we get the error ‘Uncaught TypeError: Cannot add property bar, object is not extensible’ if we try to add a property to a non-extensible object.
We can check if an object is extensible by writing:
console.log(Object.isExtensible(obj));
If it returns true
, then we can add properties to it.
Conclusion
We can do various things with proxies, like controlling access to an object and more.
For other operations, we don’t need proxies. The Object
constructor has static methods to let us control object operations.